package il.ac.tau.cg.ex3.math.shapes;

import il.ac.tau.cg.ex3.math.Params;
import il.ac.tau.cg.ex3.math.Point;
import il.ac.tau.cg.ex3.math.Vector;
import il.ac.tau.cg.ex3.utils.octree.BoundingBox;

import java.util.SortedSet;
import java.util.TreeSet;

public class Box extends Shape
{
	private Point p0;
	private Point p1;
	private Point p2;
	private Point p3;
	
	public Box(Point p0, Point p1, Point p2, Point p3)
	{
		this.p0 = p0;
		this.p1 = p1;
		this.p2 = p2;
		this.p3 = p3;
	}

	public void setP0(Point p0)
	{
		this.p0 = p0;
	}
	
	public Point getP0()
	{
		return p0;
	}
	
	public void setP1(Point p1)
	{
		this.p1 = p1;
	}
	
	public Point getP1()
	{
		return p1;
	}
	
	public void setP2(Point p2)
	{
		this.p2 = p2;
	}
	
	public Point getP2()
	{
		return p2;
	}
	
	public void setP3(Point p3)
	{
		this.p3 = p3;
	}
	
	public Point getP3()
	{
		return p3;
	}
	
	@Override
	public SortedSet<Double> intersect(Point origin, Vector direction)
	{
		Point p4 = p2.add(new Vector(p0, p1));
		Point p5 = p4.add(new Vector(p0, p3));
		Point p6 = p3.add(new Vector(p0, p2));
		
		Rectangle r1 = new Rectangle(p0, p1, p2);
		Rectangle r2 = new Rectangle(p0, p3, p1);
		Rectangle r3 = new Rectangle(p4, p1, p5);
		Rectangle r4 = new Rectangle(p4, p5, p2);
		Rectangle r5 = new Rectangle(p6, p5, p3);
		Rectangle r6 = new Rectangle(p6, p3, p2);

		SortedSet<Double> result = new TreeSet<Double>();
		
		result.addAll(r1.intersect(origin, direction));
		result.addAll(r2.intersect(origin, direction));
		result.addAll(r3.intersect(origin, direction));
		result.addAll(r4.intersect(origin, direction));
		result.addAll(r5.intersect(origin, direction));
		result.addAll(r6.intersect(origin, direction));
		
		return result;
	}

	@Override
	public Vector getNormal(Point p)
	{
		return getRectangle(p).getNormal(p);
	}

	@Override
	public Params getParams(Point p)
	{
		return getRectangle(p).getParams(p);
	}
	
	private Rectangle getRectangle(Point p)
	{
		Point p4 = p2.add(new Vector(p0, p1));
		Point p5 = p4.add(new Vector(p0, p3));
		Point p6 = p3.add(new Vector(p0, p2));
		
		Rectangle r1 = new Rectangle(p0, p1, p2);
		Rectangle r2 = new Rectangle(p0, p3, p1);
		Rectangle r3 = new Rectangle(p4, p1, p5);
		Rectangle r4 = new Rectangle(p4, p5, p2);
		Rectangle r5 = new Rectangle(p6, p5, p3);
		Rectangle r6 = new Rectangle(p6, p3, p2);
		
		Point center = p3.interpolate(p4, 0.5);
		Vector direction = new Vector(center, p);
		
		if (!r1.intersect(center, direction).isEmpty())	return r1;
		if (!r2.intersect(center, direction).isEmpty())	return r2;
		if (!r3.intersect(center, direction).isEmpty())	return r3;
		if (!r4.intersect(center, direction).isEmpty())	return r4;
		if (!r5.intersect(center, direction).isEmpty())	return r5;
		if (!r6.intersect(center, direction).isEmpty())	return r6;
		
		throw new IllegalStateException();
	}
	
	@Override
	public Box clone()
	{
		return new Box(p0.clone(), p1.clone(), p2.clone(), p3.clone());
	}

	@Override
	public BoundingBox getBoundingBox()
	{
		Point p4 = p2.add(new Vector(p0, p1));
		Point p5 = p4.add(new Vector(p0, p3));
		Point p6 = p3.add(new Vector(p0, p2));
		Point p7 = p3.add(new Vector(p0, p1));
		
		double minX = Math.min(Math.min(Math.min(p0.getX(), p1.getX()), Math.min(p2.getX(), p3.getX())),
				               Math.min(Math.min(p4.getX(), p5.getX()), Math.min(p6.getX(), p7.getX())));
		
		double maxX = Math.max(Math.max(Math.max(p0.getX(), p1.getX()), Math.max(p2.getX(), p3.getX())),
	                           Math.max(Math.max(p4.getX(), p5.getX()), Math.max(p6.getX(), p7.getX())));
		
		double minY = Math.min(Math.min(Math.min(p0.getY(), p1.getY()), Math.min(p2.getY(), p3.getY())),
   	                           Math.min(Math.min(p4.getY(), p5.getY()), Math.min(p6.getY(), p7.getY())));

		double maxY = Math.max(Math.max(Math.max(p0.getY(), p1.getY()), Math.max(p2.getY(), p3.getY())),
		                       Math.max(Math.max(p4.getY(), p5.getY()), Math.max(p6.getY(), p7.getY())));
		
		double minZ = Math.min(Math.min(Math.min(p0.getZ(), p1.getZ()), Math.min(p2.getZ(), p3.getZ())),
                               Math.min(Math.min(p4.getZ(), p5.getZ()), Math.min(p6.getZ(), p7.getZ())));

		double maxZ = Math.max(Math.max(Math.max(p0.getZ(), p1.getZ()), Math.max(p2.getZ(), p3.getZ())),
                               Math.max(Math.max(p4.getZ(), p5.getZ()), Math.max(p6.getZ(), p7.getZ())));
		
		return new BoundingBox(minX, maxX, minY, maxY, minZ, maxZ);
	}
}
